package com.lubase.metadata.service.impl;

import com.alibaba.fastjson.JSON;
import com.lubase.metadata.model.*;
import com.lubase.metadata.service.RelationMapService;
import com.lubase.model.DbEntity;
import com.lubase.model.EDBEntityState;
import com.lubase.orm.QueryOption;
import com.lubase.orm.TableFilter;
import com.lubase.model.DbCollection;
import com.lubase.orm.service.DataAccess;
import com.lubase.orm.util.TableFilterWrapper;
import com.lubase.orm.util.TypeConverterUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RelationMapServiceImpl implements RelationMapService {
    @Autowired
    DataAccess dataAccess;

    @Override
    public MapEntityRelationModel getTableRelationMapOfDatabase(String databaseId, EModelType modelType) {
        if (databaseId == null || databaseId.isEmpty()) {
            return null;
        }
        MapEntityRelationModel mapEntityRelationModel = new MapEntityRelationModel();
        mapEntityRelationModel.setTableModelList(getTableList(databaseId, null, modelType));
        mapEntityRelationModel.setRelationshipList(getConstraintList(databaseId));
        mapEntityRelationModel.setRelationMapList(getTableLayoutList(databaseId));
        // 处理表节点类型
        List<String> refTableList = new ArrayList<>();
        List<MapConstraintModel> validatedList = new ArrayList<>();
        for (MapConstraintModel constraintModel : mapEntityRelationModel.getRelationshipList()) {
            String currentTable = constraintModel.getPkColumnId().split("\\.")[0];
            String refTable = constraintModel.getFkColumnId().split("\\.")[0];
            if (mapEntityRelationModel.getTableModelList().stream().anyMatch(tableModel -> tableModel.getTableCode().equals(refTable))
                    && mapEntityRelationModel.getTableModelList().stream().anyMatch(tableModel -> tableModel.getTableCode().equals(currentTable))) {
                validatedList.add(constraintModel);
            }
            refTableList.add(constraintModel.getFkColumnId().split("\\.")[0]);
            refTableList.add(constraintModel.getPkColumnId().split("\\.")[0]);

        }
        mapEntityRelationModel.getTableModelList().forEach(tableModel -> {
            if (refTableList.contains(tableModel.getTableCode())) {
                // 如果有关联关系则是一般节点，否则为孤岛节点
                tableModel.setNodeType(1);
            } else {
                tableModel.setNodeType(2);
            }
        });
        mapEntityRelationModel.setRelationshipList(validatedList);
        return mapEntityRelationModel;
    }

    List<MapLayoutModel> getTableLayoutList(String businessId) {
        List<MapLayoutModel> relationMapList = new ArrayList<>();
        QueryOption queryOption = new QueryOption("mt_relation_map");
        queryOption.setFixField("map_setting");
        queryOption.setTableFilter(new TableFilter("business_id", businessId));
        List<DbEntity> list = dataAccess.queryAllData(queryOption).getData();
        if (!list.isEmpty()) {
            String mapSetting = TypeConverterUtils.object2String(list.get(0).get("map_setting"), "");
            if (!StringUtils.isEmpty(mapSetting)) {
                relationMapList = JSON.parseArray(mapSetting, MapLayoutModel.class);
            }
        }
        return relationMapList;
    }

    List<MapConstraintModel> getConstraintList(String databaseId) {
        QueryOption queryOption = new QueryOption("mt_constraint");
        queryOption.setFixField("table_code,column_code,constraint_type,ref_table_code,ref_column_code,from_database");
        queryOption.setTableFilter(new TableFilter("ms_database_id", databaseId));
        DbCollection collConstraint = dataAccess.queryAllData(queryOption);
        return getConstraintList(collConstraint);
    }

    List<MapConstraintModel> getConstraintList(DbCollection collConstraint) {
        List<MapConstraintModel> constraintList = new ArrayList<>();
        collConstraint.getData().parallelStream().forEach(entity -> {
            MapConstraintModel constraintModel = new MapConstraintModel();
            constraintModel.setPkColumnId(String.format("%s.%s", entity.get("table_code").toString(), entity.get("column_code").toString()));
            constraintModel.setFkColumnId(String.format("%s.%s", entity.get("ref_table_code").toString(), entity.get("ref_column_code").toString()));
            constraintModel.setConstraintType(TypeConverterUtils.object2String(entity.get("constraint_type"), ""));
            constraintModel.setFromDatabase((Integer) entity.get("from_database"));
            constraintList.add(constraintModel);
        });
        return constraintList;
    }

    List<MapTableModel> getTableList(String databaseId, String tableCodes, EModelType modelType) {
        TableFilterWrapper filterWrapper = TableFilterWrapper.and();
        if (!StringUtils.isEmpty(tableCodes)) {
            filterWrapper.in("table_code", tableCodes);
        }
        filterWrapper.eq("ms_database_id", databaseId);

        if (modelType.equals(EModelType.BizModel) || modelType.equals(EModelType.SystemModel)) {
            filterWrapper.eq("model_type", modelType.getType());
        }

        QueryOption queryOption = new QueryOption("mt_table");
        queryOption.setFixField("table_code,table_name,alias_name,model_type");
        queryOption.setTableFilter(filterWrapper.build());
        DbCollection collTable = dataAccess.queryAllData(queryOption);
        List<MapTableModel> tableList = new ArrayList<>();
        collTable.getData().forEach(entity -> {
            MapTableModel tableModel = new MapTableModel();
            tableModel.setTableCode(entity.get("table_code").toString());
            tableModel.setTableId(entity.getId().toString());
            String aliasName = TypeConverterUtils.object2String(entity.get("alias_name"), "");
            String tableName = TypeConverterUtils.object2String(entity.get("table_name"), "");
            if (!aliasName.isEmpty()) {
                tableModel.setTableName(aliasName);
            } else if (!tableName.isEmpty()) {
                tableModel.setTableName(tableName);
            } else {
                tableModel.setTableName(entity.get("table_code").toString());
            }
            tableModel.setColumnList(new ArrayList<>());
            tableList.add(tableModel);
        });

        filterWrapper = TableFilterWrapper.and();
        if (!StringUtils.isEmpty(tableCodes)) {
            filterWrapper.in("table_code", tableCodes);
        }
        filterWrapper.eq("ms_database_id", databaseId);
        queryOption = new QueryOption("mt_column");
        queryOption.setFixField("table_code,alias_name,column_code,column_name,column_type,memo,codedata_tag");
        queryOption.setTableFilter(filterWrapper.build());
        DbCollection collColumn = dataAccess.queryAllData(queryOption);
        collColumn.getData().forEach(entity -> {
            MapTableModel tableModel = tableList.stream().filter(t -> t.getTableCode().equals(entity.get("table_code").toString())).findFirst().orElse(null);
            if (tableModel != null) {
                MapColumnModel columnModel = new MapColumnModel();
                columnModel.setColumnCode(entity.get("column_code").toString());
                columnModel.setColumnType(entity.get("column_type").toString());
                columnModel.setMemo(TypeConverterUtils.object2String(entity.get("memo"), ""));
                columnModel.setCodeDataTag(TypeConverterUtils.object2Integer(entity.get("codedata_tag"), 0));
                String aliasName = TypeConverterUtils.object2String(entity.get("alias_name"), "");
                String columnName = TypeConverterUtils.object2String(entity.get("column_name"), "");
                if (!aliasName.isEmpty()) {
                    columnModel.setColumnName(aliasName);
                } else if (!columnName.isEmpty()) {
                    columnModel.setColumnName(columnName);
                } else {
                    columnModel.setColumnName(entity.get("column_code").toString());
                }
                columnModel.setColumnId(String.format("%s.%s", entity.get("table_code").toString(), entity.get("column_code").toString()));
                tableModel.getColumnList().add(columnModel);
            }
        });
        return tableList;
    }

    @Override
    public MapEntityRelationModel getTableRelationMapOfTable(String tableId) {
        // 检查参数是否为空
        if (StringUtils.isEmpty(tableId)) {
            log.error("getTableRelationMapOfTable: tableId is empty");
            return null;
        }
        QueryOption queryOption = new QueryOption("mt_table");
        queryOption.setTableFilter(new TableFilter("id", tableId));
        queryOption.setFixField("id,ms_database_id,table_code");
        DbCollection collection = dataAccess.queryAllData(queryOption);
        if (collection.getData().isEmpty()) {
            log.error("getTableRelationMapOfTable: tableId is not exists");
            return null;
        }
        String databaseId = collection.getData().get(0).get("ms_database_id").toString();
        // 1 先获取关系
        queryOption = new QueryOption("mt_constraint");
        queryOption.setFixField("table_code,column_code,constraint_type,ref_table_code,ref_column_code,from_database");
        TableFilterWrapper filterWrapper = TableFilterWrapper.and();
        filterWrapper.eq("ms_database_id", databaseId);
        TableFilterWrapper filterWrapper1 = TableFilterWrapper.or();
        filterWrapper1.eq("table_code", collection.getData().get(0).get("table_code"));
        filterWrapper1.eq("ref_table_code", collection.getData().get(0).get("table_code"));
        filterWrapper.addFilter(filterWrapper1.build());
        queryOption.setTableFilter(filterWrapper.build());
        DbCollection collConstraint = dataAccess.queryAllData(queryOption);

        List<String> tableCodeList = new ArrayList<>();
        collConstraint.getData().forEach(entity -> {
            tableCodeList.add(entity.get("table_code").toString());
            tableCodeList.add(entity.get("ref_table_code").toString());
        });
        // 如果当前表没有关联关系，则增加当前表，避免查询全部数据
        if (collConstraint.getData().isEmpty()) {
            tableCodeList.add(collection.getData().get(0).get("table_code").toString());
        }

        MapEntityRelationModel mapEntityRelationModel = new MapEntityRelationModel();
        mapEntityRelationModel.setRelationshipList(getConstraintList(collConstraint));
        List<MapTableModel> tableList = getTableList(databaseId, tableCodeList.stream().distinct().collect(Collectors.joining(",")), EModelType.All);
        tableList.forEach(mapTableModel -> {
            if (collection.getData().stream().anyMatch(entity -> entity.getId().toString().equals(mapTableModel.getTableId()))) {
                mapTableModel.setIsReferenceNode(0);
            } else {
                mapTableModel.setIsReferenceNode(1);
            }
        });
        mapEntityRelationModel.setTableModelList(tableList);
        mapEntityRelationModel.setRelationMapList(getTableLayoutList(tableId));
        return mapEntityRelationModel;
    }

    @Override
    public MapEntityRelationModel getTableRelationMapOfScene(String sceneId) {
        // 检查参数是否为空
        if (StringUtils.isEmpty(sceneId)) {
            log.error("getTableRelationMapOfScene: sceneId is empty");
            return null;
        }
        QueryOption queryOption = new QueryOption("mt_scene_table");
        queryOption.setTableFilter(new TableFilter("mt_scene_id", sceneId));
        queryOption.setFixField("id,ms_database_id,mt_table_id,mt_table_id.table_code");
        DbCollection collection = dataAccess.queryAllData(queryOption);

        return getTableRelationMapOfScene(collection, sceneId);
    }

    @Override
    public MapEntityRelationModel getTableRelationMapOfApply(String applyId) {
        // 检查参数是否为空
        if (StringUtils.isEmpty(applyId)) {
            log.error("getTableRelationMapOfScene: applyId is empty");
            return null;
        }
        QueryOption queryOption = new QueryOption("mt_lake_detail");
        queryOption.setTableFilter(new TableFilter("mt_lake_apply_id", applyId));
        queryOption.setFixField("id,ms_database_id,mt_table_id,mt_table_id.table_code");
        DbCollection collection = dataAccess.queryAllData(queryOption);

        return getTableRelationMapOfScene(collection, applyId);
    }

    private MapEntityRelationModel getTableRelationMapOfScene(DbCollection collection, String mapId) {
        if (collection.getData().isEmpty()) {
            log.error("getTableRelationMapOfTable: applyId is not exists");
            return null;
        }
        String databaseId = collection.getData().get(0).get("ms_database_id").toString();
        // 1 先获取关系
        QueryOption queryOption = new QueryOption("mt_constraint");
        queryOption.setFixField("table_code,column_code,constraint_type,ref_table_code,ref_column_code,from_database");
        TableFilterWrapper filterWrapper = TableFilterWrapper.or();
        collection.getData().forEach(entity -> {
            TableFilterWrapper filterWrapper1 = TableFilterWrapper.and();
            filterWrapper1.eq("ms_database_id", databaseId);

            TableFilterWrapper filterWrapper2 = TableFilterWrapper.or();
            filterWrapper2.eq("table_code", collection.getData().get(0).get("mt_table_id#table_code"));
            filterWrapper2.eq("ref_table_code", collection.getData().get(0).get("mt_table_id#table_code"));
            filterWrapper1.addFilter(filterWrapper2.build());

            filterWrapper.addFilter(filterWrapper1.build());
        });
        queryOption.setTableFilter(filterWrapper.build());
        DbCollection collConstraint = dataAccess.queryAllData(queryOption);

        List<String> tableCodeList = new ArrayList<>();
        collConstraint.getData().forEach(entity -> {
            tableCodeList.add(entity.get("table_code").toString());
            tableCodeList.add(entity.get("ref_table_code").toString());
        });
        // 如果当前表没有关联关系，则增加当前表，避免查询全部数据
        if (collConstraint.getData().isEmpty()) {
            collection.getData().forEach(entity -> {
                tableCodeList.add(entity.get("mt_table_id#table_code").toString());
            });
        }
        MapEntityRelationModel mapEntityRelationModel = new MapEntityRelationModel();
        mapEntityRelationModel.setRelationshipList(getConstraintList(collConstraint));
        List<MapTableModel> tableList = getTableList(databaseId, tableCodeList.stream().distinct().collect(Collectors.joining(",")), EModelType.All);
        tableList.forEach(mapTableModel -> {
            if (collection.getData().stream().anyMatch(entity -> entity.get("mt_table_id").toString().equals(mapTableModel.getTableId()))) {
                mapTableModel.setIsReferenceNode(0);
            } else {
                mapTableModel.setIsReferenceNode(1);
            }
        });
        mapEntityRelationModel.setTableModelList(tableList);
        mapEntityRelationModel.setRelationMapList(getTableLayoutList(mapId));
        return mapEntityRelationModel;
    }

    @Override
    public Integer setTableRelationMap(String businessId, EMapType mapType, List<MapLayoutModel> relationMapList) {
        // 检查参数是否为空
        if (StringUtils.isEmpty(businessId) || mapType == null || relationMapList == null || relationMapList.isEmpty()) {
            log.error("setTableRelationMap: businessId or mapType or relationMapList is empty");
            return 0;
        }
        QueryOption queryOption = new QueryOption("mt_relation_map");
        queryOption.setFixField("map_setting");
        queryOption.setTableFilter(new TableFilter("business_id", businessId));
        DbCollection collection = dataAccess.queryAllData(queryOption);
        if (collection.getData().isEmpty()) {
            DbEntity entity = collection.newEntity();
            entity.put("business_id", businessId);
            entity.put("map_type", mapType);
            entity.put("map_setting", JSON.toJSONString(relationMapList));
            entity.setState(EDBEntityState.Added);
            collection.getData().add(entity);
            return dataAccess.update(collection);
        } else {
            DbEntity entity = collection.getData().get(0);
            entity.put("map_setting", JSON.toJSONString(relationMapList));
            entity.setState(EDBEntityState.Modified);
            return dataAccess.update(collection);
        }
    }
}
